﻿using DevExpress.ExpressApp.DC;
using DevExpress.Persistent.Validation;
using Newtonsoft.Json;
using RulesEngine.Models;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace EasyXaf.LowCode.RulesEngineEditors.Models;

[DomainComponent]
[XafDisplayName("工作流")]
public class WorkflowObject : ModelObject
{
    private string _workflowName;
    private NestedRuleExecutionMode _nestedRuleExecutionMode;

    [RuleRequiredField]
    [XafDisplayName("名称")]
    public string WorkflowName
    {
        get => _workflowName;
        set => SetPropertyValue(ref _workflowName, value);
    }

    [XafDisplayName("嵌套规则执行方式")]
    public NestedRuleExecutionMode NestedRuleExecutionMode
    {
        get => _nestedRuleExecutionMode;
        set => SetPropertyValue(ref _nestedRuleExecutionMode, value);
    }

    [XafDisplayName("输入参数")]
    public ObservableCollection<InputParamObject> InputParams { get; } = new();

    [XafDisplayName("全局参数")]
    public ObservableCollection<ScopedParamObject> GlobalParams { get; } = new();

    [XafDisplayName("输出类型")]
    public ObservableCollection<OutputType> OutputTypes { get; } = new();

    [Browsable(false)]
    public ObservableCollection<RuleObject> Rules { get; } = new();

    [Browsable(false)]
    public RuleObject LastClonedRule { get; set; }

    public WorkflowObject()
    {
        Rules.Add(new StartObject
        {
            Workflow = this
        });
    }

    public override IEnumerable<ValidationResult> Validate()
    {
        var validationResults = new List<ValidationResult>(base.Validate());

        if (string.IsNullOrWhiteSpace(WorkflowName))
        {
            validationResults.Add(new ValidationResult("工作流名称不能为空", ValidationResultLevel.Error));
        }

        foreach (var inputParam in InputParams)
        {
            foreach (var validationResult in inputParam.Validate())
            {
                validationResults.Add(validationResult);
            }
        }

        foreach (var globalParam in GlobalParams)
        {
            foreach (var validationResult in globalParam.Validate())
            {
                validationResults.Add(validationResult);
            }
        }

        foreach (var outputType in OutputTypes)
        {
            foreach (var validationResult in outputType.Validate())
            {
                validationResults.Add(validationResult);
            }
        }

        foreach (var rule in Rules)
        {
            if (rule is StartObject)
            {
                continue;
            }

            rule.Validate();
        }

        ValidationResults = validationResults;
        OnValidationResultsChanged();

        return validationResults;
    }

    public RulesEngineWorkflow ToWorkflow()
    {
        var workflow = new RulesEngineWorkflow
        {
            WorkflowName = WorkflowName,
            RuleExpressionType = RuleExpressionType.LambdaExpression,
            Properties = new Dictionary<string, object>(),
            GlobalParams = new List<ScopedParam>(),
            Rules = new List<Rule>(),
        };

        foreach (var parameter in GlobalParams)
        {
            ((List<ScopedParam>)workflow.GlobalParams).Add(new ScopedParam
            {
                Name = parameter.Name,
                Expression = parameter.Expression,
            });
        }

        foreach (var ruleObject in Rules)
        {
            if (ruleObject is StartObject)
            {
                continue;
            }

            ((List<Rule>)workflow.Rules).Add(ruleObject.ToRule());
        }

        workflow.Properties["nestedRuleExecutionMode"] = (int)NestedRuleExecutionMode;

        workflow.Properties["inputParams"] = InputParams
            .Select(p => new { p.Name, p.Description, p.TypeName, p.IsCollection })
            .ToArray();

        workflow.Properties["globalParams"] = GlobalParams
            .Select(p => new { p.Name, p.Description })
            .ToArray();

        workflow.Properties["outputTypes"] = OutputTypes
            .Select(p => new { p.Name })
            .ToArray();

        return workflow;
    }

    public static WorkflowObject FromWorkflow(RulesEngineWorkflow workflow)
    {
        var workflowObject = new WorkflowObject
        {
            WorkflowName = workflow.WorkflowName,
        };

        if (workflow.GlobalParams != null)
        {
            foreach (var parameter in workflow.GlobalParams)
            {
                workflowObject.GlobalParams.Add(new ScopedParamObject
                {
                    Name = parameter.Name,
                    Expression = parameter.Expression,
                });
            }
        }

        if (workflow.Properties != null)
        {
            if (workflow.Properties.TryGetValue("nestedRuleExecutionMode", out object nestedRuleExecutionModeValue))
            {
                if (int.TryParse(nestedRuleExecutionModeValue.ToString(), out int mode))
                {
                    workflowObject.NestedRuleExecutionMode = mode switch
                    {
                        1 => NestedRuleExecutionMode.Performance,
                        _ => NestedRuleExecutionMode.All
                    };
                }
            }

            if (workflow.Properties.TryGetValue("inputParams", out object inputParamsValue))
            {
                var json = JsonConvert.SerializeObject(inputParamsValue);
                var inputParams = JsonConvert.DeserializeObject<List<InputParamObject>>(json);
                if (inputParams != null)
                {
                    foreach (var inputParam in inputParams)
                    {
                        workflowObject.InputParams.Add(inputParam);
                    }
                }
            }

            if (workflow.Properties.TryGetValue("globalParams", out object globalParamsValue))
            {
                var json = JsonConvert.SerializeObject(globalParamsValue);
                var globalParamObjects = JsonConvert.DeserializeObject<List<ScopedParamObject>>(json);
                if (globalParamObjects != null)
                {
                    foreach (var globalParamObject in globalParamObjects)
                    {
                        var globalParam = workflowObject.GlobalParams.FirstOrDefault(p => p.Name == globalParamObject.Name);
                        if (globalParam != null)
                        {
                            globalParam.Description = globalParamObject.Description;
                        }
                    }
                }
            }

            if (workflow.Properties.TryGetValue("outputTypes", out object outputTypesValue))
            {
                var json = JsonConvert.SerializeObject(outputTypesValue);
                var outputTypes = JsonConvert.DeserializeObject<List<OutputType>>(json);
                if (outputTypes != null)
                {
                    foreach (var outputType in outputTypes)
                    {
                        workflowObject.OutputTypes.Add(outputType);
                    }
                }
            }
        }

        if (workflow.Rules != null)
        {
            foreach (var rule in workflow.Rules)
            {
                workflowObject.Rules.Add(RuleObject.FromRule(rule, null, workflowObject));
            }
        }

        return workflowObject;
    }
}
